package com.core.async;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

import com.core.action.TimeAction;
import com.core.interfaces.IMessageExecutorPool;
import com.core.utils.MethodUtil;

/**
 * 异步服务实例
 * 用于从一个线程中丢出某个任务让另外一个线程执行 执行完后丢回给原线程继续去执行
 * 可用于插入数据库等耗时操作
 * @author King
 *
 */
public class AsyncServiceImpl extends ApplicationContext implements IAsyncService {

	/**方法的缓存**/
	private final Map<String, Method> methodCacheMap = new ConcurrentHashMap<String, Method>();
	
	/**类缓存**/
	private final ConcurrentHashMap<String, Object> targetCacheMap  = new ConcurrentHashMap<String, Object>();
	
	
	/* (non-Javadoc)
	 * @see core.async.context.IAsyncService#init(java.util.concurrent.ExecutorService)
	 */
	@Override
	public void init(IMessageExecutorPool mainExecutor,int asyncPoolSize) {
		asyncExecutor = new ScheduledThreadPoolExecutor(asyncPoolSize,new ThreadFactory() {
			@Override
			public Thread newThread(Runnable r) {
				Thread thread = new Thread(r);
				thread.setName("asyncThread");
				return thread;
			}
		});
		super.init(mainExecutor);
	}
	
	/* (non-Javadoc)
	 * @see core.async.context.IAsyncService#addAsyncBackWork(core.async.AsyncObject, java.lang.String, java.lang.Object)
	 */
	@Override
	public void addAsyncBackWork(AsyncObject target,String method,Object... params)
	{
		asyncExecutor.execute(new AsyncWorker(target, method, params,true));
	}
	
	
	/* (non-Javadoc)
	 * @see core.async.context.IAsyncService#addAsyncTimeWork(core.pub.TimeAction)
	 */
	@Override
	public void addAsyncTimeWork(TimeAction action)
	{
		if(!(asyncExecutor instanceof ScheduledThreadPoolExecutor))
			return;
		TimeAction timeAction = (TimeAction) action;
		ScheduledFuture<?> future = ((ScheduledThreadPoolExecutor)this.asyncExecutor).scheduleWithFixedDelay(action,
						timeAction.getIntervalMill(),
						timeAction.getIntervalMill(), TimeUnit.MILLISECONDS);
		timeAction.setFutrue(future);
	}
	
	/* (non-Javadoc)
	 * @see core.async.context.IAsyncService#addAsyncWork(java.lang.Object, java.lang.String, java.lang.Object)
	 */
	@Override
	public void addAsyncWork(Object target,String method,Object... params)
	{
		asyncExecutor.execute(new AsyncWorker(target, method, params,false));
	}
	
	/* (non-Javadoc)
	 * @see core.async.context.IAsyncService#addAsyncWork(java.lang.Class, java.lang.String, java.lang.Object)
	 */
	@Override
	public void addAsyncWork(Class target,String method,Object... params)
	{
		Object obj = getTargetObj(target, true);
		addAsyncWork(obj, method, params);
	}
	
	
	private Object getTargetObj(Class target,boolean cache)
	{
		 String classKey = target.getSimpleName();
		 Object targetObj = targetCacheMap.get(classKey);
		 if(targetObj==null)
		 {
			 targetObj = newObject(target);
			 if(cache)
				 targetCacheMap.put(classKey, targetObj);
		 }
		 return targetObj;
	}
	
	 /**
     * class instantiation object
     * @param clzss
     * @return
     */
    private Object newObject(Class clzss) {
        try {
            Constructor constructor = clzss.getConstructor();
            if (constructor == null) {
                throw new IllegalArgumentException("target not have default constructor function");
            }
            // Instance target object
            return clzss.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
	
	class AsyncWorker implements Runnable,Comparable<AsyncWorker>
	{
		Object target;
		
		private String method;

		private Object[] params;
		
		private boolean needBackNotify;
		/**
		 * 回调回去的主线程名称
		 */
		private String mainThreadName;
		
		protected AsyncWorker(Object target,String method,Object[] params,boolean needBackNotify)
		{
			this.target = target;
			this.method = method;
			this.params = params;
			this.needBackNotify = needBackNotify;
			this.mainThreadName = Thread.currentThread().getName();
		}
		@Override
		public void run() 
		{
			try {
				if (target == null)
					throw new RuntimeException("target object is null");
				Class clazz = target.getClass();
				String methodKey = MethodUtil.getClassMethodKey(clazz, params, method);
				Method targetMethod = methodCacheMap.get(methodKey);
				if (targetMethod == null) {
					targetMethod = MethodUtil.getTargetMethod(clazz, params, method);
					if (targetMethod != null) {
						methodCacheMap.put(methodKey, targetMethod);
					}
				}
				if (targetMethod == null) {
					throw new IllegalArgumentException("target method is null");
				}
				targetMethod.invoke(target, params);
				if(needBackNotify)
				{
					mainExecutor.getExecutor(mainThreadName).exeTask((Runnable)target);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		@Override
		public int compareTo(AsyncWorker o) {
			return 0;
		}
	}

}
